Skip to content

feat: add prepare-for-signing and apply-signature for enterprise HSM signing#222

Open
bryan-anthropic wants to merge 1 commit intomainfrom
feat/external-signing
Open

feat: add prepare-for-signing and apply-signature for enterprise HSM signing#222
bryan-anthropic wants to merge 1 commit intomainfrom
feat/external-signing

Conversation

@bryan-anthropic
Copy link
Copy Markdown
Collaborator

Summary

Enterprise HSM signers (GaraSign, ESRP, SignServer, Venafi) produce MCPB bundles that Claude Desktop rejects. The root cause: external signers append PKCS#7 signatures after the ZIP EOCD without updating comment_length, causing adm-zip to reject the file with "Invalid comment length."

This PR adds two new CLI commands that implement a pre-signing preparation workflow — the signer operates on content that already has the correct EOCD metadata, so no post-signing fixup is needed:

# Step 1: Pre-allocate signature space in the ZIP EOCD
mcpb prepare-for-signing bundle.mcpb

# Step 2: Sign with any enterprise HSM (detached PKCS#7, DER format)
# (GaraSign, ESRP, SignServer, Venafi, Azure SignTool, etc.)

# Step 3: Embed the detached signature into the pre-allocated slot
mcpb apply-signature bundle.mcpb --signature sig.p7s

Supersedes #220. Complementary to #195 — once that verify fix merges, mcpb verify will validate bundles produced by this workflow with no additional changes needed (the signature covers content with the correct comment_length, so no EOCD reversal is required).

Why this works

The key insight is when the EOCD is modified relative to signing:

Approach EOCD modified Signature covers Result
External signer (broken) Never comment_length=0 adm-zip rejects trailing bytes
fix-signature (#220) After signing comment_length=0 Works but signed ≠ delivered
This PR Before signing comment_length=16384 Signed = delivered, valid ZIP

Reference implementation

This generalizes the pattern that Microsoft's Azure MCP Server team already ships in production:

Microsoft's signed Azure MCP bundles (beta.22 through beta.33) have been installing successfully in Claude Desktop using this exact pattern. Our output is byte-for-byte format-compatible with theirs (16384-byte padded signature block, MCPB_SIG_V1/MCPB_SIG_END markers, zero-padded DER PKCS#7).

What's included

src/node/sign.ts:

  • prepareForExternalSigning() — sets EOCD comment_length to 16384, validates not already signed/prepared
  • applyExternalSignature() — embeds detached PKCS#7 into pre-allocated slot with zero-padding
  • MAX_SIG_BLOCK_SIZE constant (16384, matching Microsoft's pipeline)

src/cli/cli.ts:

  • mcpb prepare-for-signing <file> — with -o/--output option
  • mcpb apply-signature <file> -s <sig.p7s> — with -o/--output option

test/sign.e2e.test.ts:

  • 8 new tests covering full roundtrip, rejection of signed/prepared/unprepared bundles, oversized signatures, padding correctness, adm-zip strict validation, and output path handling

Test results

  • 227 tests passing (219 existing + 8 new), zero regressions
  • Manual validation with real enterprise-signed bundle (1.4MB, 729 files)
  • Structural comparison with Microsoft's signed Azure MCP bundles confirms format match
  • Claude Desktop install test: PASS (bundle installs and initializes server)

Test plan

  • All 227 tests pass (npm test)
  • prepare-for-signing sets EOCD comment_length to 16384
  • apply-signature produces valid ZIP (Python zipfile accepts)
  • Signature block is exactly 16384 bytes (padded)
  • file_size == eocd_offset + 22 + comment_length (adm-zip check)
  • Output format matches Microsoft's Azure MCP signed bundles
  • Claude Desktop install test with externally-signed bundle

🤖 Generated with Claude Code

/cc @asklar @vcolin7 — this generalizes the signing pattern from your Azure MCP Server pipeline (eng/scripts/Stage-McpbForSigning.ps1 + Apply-McpbSignatures.ps1) into the CLI. Would appreciate your review since your bundles are the reference implementation.

…rise HSM signing

Enterprise signers (GaraSign, ESRP, SignServer, Venafi) produce MCPB bundles
that Claude Desktop rejects because they append PKCS#7 signatures after the
ZIP EOCD without updating comment_length. This adds a two-step workflow that
pre-allocates the EOCD comment space before signing, so the delivered file is
byte-identical to what was signed:

  1. mcpb prepare-for-signing <bundle> — sets EOCD comment_length to 16384
  2. Enterprise signer signs the prepared file (detached PKCS#7)
  3. mcpb apply-signature <bundle> --signature <sig.p7s> — embeds signature

Based on Microsoft's Azure MCP Server production pipeline
(eng/scripts/Stage-McpbForSigning.ps1 + Apply-McpbSignatures.ps1) which has
been shipping signed bundles successfully since beta.22.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, reopen this pull request to trigger a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant